﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UIWindow.Domains;
using UIWindow.Lucifer;

namespace UIWindow.DeviceCommunications
{
    public class CommSerialPort : ICommunication
    {
        public List<byte> ByteCache { get; set; }
        public bool IsConnect { get; }

        public event CustomDelegates.DropAlarm DropAlarms;

        private SerialPort _serialPort;
        private string _com;
        private int _timeout;
        private int _index;

        /// <summary>
        /// 串口通信
        /// </summary>
        /// <param name="comId">串口号</param>
        /// <param name="baudRate">串行波特率</param>
        /// <param name="index">索引</param>
        /// <param name="parity">奇偶校验位</param>
        /// <param name="dataBits">每个字节的标准数据位长度</param>
        /// <param name="stopBits">停止位的数目</param>
        public CommSerialPort(string comId, int baudRate, int index, int outtime = 3000, Parity parity = Parity.Even, int dataBits = 8, StopBits stopBits = StopBits.One)
        {
            _com = comId;
            _serialPort = new SerialPort(comId, baudRate, parity, dataBits, stopBits);
            _serialPort.ReadBufferSize = 2048;
            _serialPort.WriteBufferSize = 1024;
            _timeout = outtime;
            _index = index;
            _serialPort.DtrEnable = true;
            _serialPort.RtsEnable = true;
        }

        public void Close()
        {
            if (_serialPort != null && _serialPort.IsOpen) _serialPort.Close();
        }

        public bool Connect()
        {
            if (!_serialPort.IsOpen)
            {
                try
                {
                    _serialPort.ReadTimeout = _timeout;
                    _serialPort.WriteTimeout = _timeout;
                    _serialPort.Open();
                }
                catch (Exception)
                {
                    return false;
                }
                return _serialPort.IsOpen;
            }
            return _serialPort.IsOpen;
        }

        public void Read(CancellationTokenSource tokenSource)
        {
            Task.Run(() =>
            {
                byte[] bytes = new byte[2048];
                while (true)
                {
                    try
                    {
                        int length = _serialPort.Read(bytes, 0, 2048);
                        ByteCache.AddRange(bytes.Take(length).ToArray());
                        if (ByteCache.Count > 2048) ByteCache.Clear();
                        if (tokenSource.Token.IsCancellationRequested) return;
                    }
                    catch (Exception ex)
                    {
                        DropAlarms(_com, 0, _index, ex.Message);
                    }
                    Thread.Sleep(100);
                }
            });
        }

        public byte[] Read()
        {
            byte[] bytes = new byte[2048];
            int length = _serialPort.Read(bytes, 0, 2048);
            return bytes.Take(length).ToArray();
        }

        public void SetTimeout(int time)
        {
            _serialPort.ReadTimeout = time;
            _serialPort.WriteTimeout = time;
            _timeout = time;
        }

        public void Write(byte[] bytes) => _serialPort.Write(bytes, 0, bytes.Length);

        public bool Write(IProtocol protocol)
        {

            try
            {
                byte[] bytes = protocol.GetStream();
                _serialPort.Write(bytes, 0, bytes.Length);
                return true;
            }
            catch (Exception ex)
            {
                DropAlarms(_com, 0, _index, ex.Message);
                return false;
            }
        }

        public T ReadWrite<T>(IProtocol protocol) where T : class, new()
        {
            Write(protocol);
            while (_serialPort.BytesToRead < 5)
            {
                Task.Delay(5).Wait();
            }
            byte[] bytes = new byte[2048];
            int length = _serialPort.Read(bytes, 0, bytes.Length);
            T model = new T();

            return model;
        }
    }
}
